'''
Created on Feb 17, 2013

@author: Petko
'''
from django.utils.importlib import import_module
from django.core.exceptions import ImproperlyConfigured
import hmac
from sententialyzer.settings import SENTENTIALYZER_ACCESS_TOKEN_KEY
from sent.utils.permissions import parseScopeList, Scope
import time
from uuid import UUID

class ErrorIds():
    SUCCESS=0
    BADREQUEST=400
    UNAUTHORIZED=401
    FORBIDDEN=403
    NOT_FOUND=404
    CONFLICT=409
    INTERNAL_ERROR=500
    
    
class AccessTokenErrors():
    INVALID_FORMAT = "Invalid access token"
    TOKEN_EXPIRED = "Token has expired"
    MISSING_SCOPE = "Expected scope "

class CommonResult(object):
    '''
    classdocs
    '''

    def __init__(self, errorId = ErrorIds.SUCCESS, result = {}, totalResults = -1):
        '''
        Constructor
        '''
        self.errorId = errorId
        self.result = result
        self.totalResults = totalResults
        
    
    def __repr__(self):
        return "CommonResult(%d %s %d)" % (self.errorId, self.result, self.totalResults)
        
class AccessToken():
    '''
    classdocs
    '''
    owner_id=str()
    owner_ext_id=str()
    expiration_time=0
    scopes=str()
    scopesSet=set()
    
    data=str()
    digest=str()
    def __init__(self):
        '''
        Constructor
        '''
        pass
    
    def getOwnerUUID(self):
        return UUID(self.owner_id)
    
    def toString(self):
        return self.data + '|' + self.digest
    
    def fromString(self, strVal):
        assert isinstance(strVal, basestring)
        token_parts = strVal.rsplit('|', 2)
        if len(token_parts) == 2:
            self.data = token_parts[0]
            self.digest = token_parts[1]
            return True
        else:
            return False
        
    def encrypt(self):
        self.data = self.owner_id + ',' + self.owner_ext_id + ',' + self.scopes + ',' + str( int( self.expiration_time ) )
        digest_maker = hmac.new(SENTENTIALYZER_ACCESS_TOKEN_KEY)
        digest_maker.update(self.data)
        self.digest = digest_maker.hexdigest()
    
    def decrypt(self):
        digest_maker = hmac.new(SENTENTIALYZER_ACCESS_TOKEN_KEY)
        digest_maker.update(self.data)
        currentDigest = digest_maker.hexdigest()
        if currentDigest == self.digest:
            data_parts = self.data.split(',')
            if len(data_parts) == 4:
                self.owner_id = data_parts[0]
                self.owner_ext_id = data_parts[1]
                self.scopes = data_parts[2]
                self.scopesSet = parseScopeList(self.scopes)
                self.expiration_time = int(data_parts[3])
                return True
            else:
                return False
        else:
            return False
    
    def containsScope(self, scope):
        if isinstance(scope, set):
            scopeSet = scope
        else:
            scopeSet = set([scope])
        
        groupScopes = set()
        requiredOpinionScopes = {}
        requiredViewScopes = {}
        for s in scopeSet:
            aScope = Scope(s)
            if aScope.isGroupScope:
                groupScopes.add(aScope)
            elif aScope.isOpinionScope and not aScope.isDefaultOpScope:
                requiredOpinionScopes[aScope.opinionId] = aScope
            elif aScope.isViewScope and not aScope.isDefaultViewScope:
                requiredViewScopes[aScope.viewId] = aScope
                
        groupValid = self.scopesSet.issuperset(groupScopes)
        if groupValid:
            for myScope in self.scopesSet:
                assert isinstance(myScope, Scope)
                
                if myScope.isOpinionScope and not myScope.isDefaultOpScope:
                    requiredOpinionScope = requiredOpinionScopes.get(myScope.opinionId, None)
                    if requiredOpinionScope:
                        if myScope.opinionPermissions.issuperset(requiredOpinionScope.opinionPermissions):
                            requiredOpinionScopes.pop(myScope.opinionId)
                        else:
                            return False
                        
                if myScope.isViewScope and not myScope.isDefaultViewScope:
                    requiredViewScope = requiredViewScopes.get(myScope.viewId, None)
                    if requiredViewScope:
                        if myScope.viewPermissions.issuperset(requiredViewScope.viewPermissions):
                            requiredViewScopes.pop(myScope.viewId)
                        else:
                            return False
                        
            return len(requiredOpinionScopes) == 0 and len(requiredViewScopes) == 0
        else:
            return False
    
def createValidToken(tokenStr, scopesNeeded = '', currentTime = time.time()):
    '''
    Validates the provided access token string and returns accessToken, error
    if successful accessToken is instance of AccessToken, error is None
    if failed accessToken is None, error is 
    @param tokenStr: access token string 
    @param scopesNeeded: String or set of strings. If provided, the function will check if the access token contains the required scope(s). 
    @param currentTime: Used to check if the token has expired
    '''
    accessToken = AccessToken()
    if accessToken.fromString(tokenStr):
        if accessToken.decrypt():
            if currentTime < accessToken.expiration_time:
                
                if not scopesNeeded or accessToken.containsScope(scopesNeeded):
                    return accessToken, None
                else:
                    return None, AccessTokenErrors.MISSING_SCOPE + str(scopesNeeded)
            else:
                return None, AccessTokenErrors.TOKEN_EXPIRED
        else:
            return None, AccessTokenErrors.INVALID_FORMAT
    else:
        return None, AccessTokenErrors.INVALID_FORMAT

def find_implementation(implName, InterfaceClass):
    
    if isinstance(implName, basestring):
        module, attr = implName.rsplit('.', 1)
        try:
            mod = import_module(module)
        except ImportError, e:
            raise ImproperlyConfigured('Error importing implementation source %s: "%s"' % (implName, e))
        try:
            AuthenticatorClass = getattr(mod, attr)
        except AttributeError, e:
            raise ImproperlyConfigured('Error importing implementation source %s: "%s"' % (implName, e))
        if issubclass(AuthenticatorClass, InterfaceClass):
            return AuthenticatorClass
        else:
            raise ImproperlyConfigured('not sublass of InterfaceClass %s' % implName)
    else:
        raise ImproperlyConfigured('implName is not a string')
    
    